/*
 * This file is part of git-commit-id-maven-plugin
 * Originally invented by Konrad 'ktoso' Malawski <konrad.malawski@java.pl>
 *
 * git-commit-id-maven-plugin is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * git-commit-id-maven-plugin is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with git-commit-id-maven-plugin.  If not, see <http://www.gnu.org/licenses/>.
 */

package pl.project13.maven.git;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Properties;
import org.apache.maven.project.MavenProject;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import pl.project13.core.jgit.DescribeResult;

/**
 * Testcases to verify that the {@link DescribeResult} works properly.
 */
public class NativeAndJGitProviderTest extends GitIntegrationTest {
  private static final String[] GIT_KEYS =
      new String[] {
        "git.build.time",
        "git.build.host",
        "git.branch",
        "git.commit.id.full",
        "git.commit.id.abbrev",
        "git.commit.id.describe",
        "git.build.user.name",
        "git.build.user.email",
        "git.commit.user.name",
        "git.commit.user.email",
        "git.commit.message.full",
        "git.commit.message.short",
        "git.commit.time",
        "git.commit.author.time",
        "git.commit.committer.time",
        "git.total.commit.count",
        "git.remote.origin.url",
        "git.local.branch.ahead",
        "git.local.branch.behind",
      };

  private static final String DEFAULT_FORMAT_STRING = "yyyy-MM-dd'T'HH:mm:ssXXX";
  private static final String ISO8601_FORMAT_STRING = "yyyy-MM-dd'T'HH:mm:ssXXX";

  @Test
  public void testCompareBasic() throws Exception {
    // Test on all available basic repos to ensure that the output is identical.
    for (AvailableGitTestRepo testRepo : AvailableGitTestRepo.values()) {
      if (testRepo != AvailableGitTestRepo.GIT_COMMIT_ID) {
        mavenSandbox
            .withParentProject("my-basic-project", "jar")
            .withNoChildProject()
            .withGitRepoInParent(testRepo)
            .create();
        MavenProject targetProject = mavenSandbox.getParentProject();
        verifyNativeAndJGit(testRepo, targetProject, DEFAULT_FORMAT_STRING);
      }
    }
  }

  @Test
  public void testCompareSubrepoInRoot() throws Exception {
    for (AvailableGitTestRepo testRepo : AvailableGitTestRepo.values()) {
      if (testRepo != AvailableGitTestRepo.GIT_COMMIT_ID) {
        // Don't create a subrepo based on the plugin repo itself.
        mavenSandbox
            .withParentProject("my-pom-project", "pom")
            .withChildProject("my-jar-module", "jar")
            .withGitRepoInParent(testRepo)
            .create();
        MavenProject targetProject = mavenSandbox.getParentProject();
        verifyNativeAndJGit(testRepo, targetProject, DEFAULT_FORMAT_STRING);
      }
    }
  }

  @Test
  public void testCompareSubrepoInChild() throws Exception {
    for (AvailableGitTestRepo testRepo : AvailableGitTestRepo.values()) {
      if (testRepo != AvailableGitTestRepo.GIT_COMMIT_ID) {
        // Don't create a subrepo based on the plugin repo itself.
        mavenSandbox
            .withParentProject("my-pom-project", "pom")
            .withChildProject("my-jar-module", "jar")
            .withGitRepoInParent(testRepo)
            .create();
        MavenProject targetProject = mavenSandbox.getChildProject();
        verifyNativeAndJGit(testRepo, targetProject, DEFAULT_FORMAT_STRING);
      }
    }
  }

  @Test
  public void testCompareIso8601Time() throws Exception {
    // Test on all available basic repos to ensure that the output is identical.
    for (AvailableGitTestRepo testRepo : AvailableGitTestRepo.values()) {
      if (testRepo != AvailableGitTestRepo.GIT_COMMIT_ID) {
        mavenSandbox
            .withParentProject("my-basic-project", "jar")
            .withNoChildProject()
            .withGitRepoInParent(testRepo)
            .create();
        MavenProject targetProject = mavenSandbox.getParentProject();
        verifyNativeAndJGit(testRepo, targetProject, ISO8601_FORMAT_STRING);
      }
    }
  }

  private void verifyNativeAndJGit(
      AvailableGitTestRepo repo, MavenProject targetProject, String formatString) throws Exception {
    setProjectToExecuteMojoIn(targetProject);

    mojo.skipPoms = false;
    mojo.dateFormat = formatString;

    DateFormat format = new SimpleDateFormat(formatString);

    mojo.useNativeGit = false;
    mojo.execute();
    Properties jgitProps = createCopy(targetProject.getProperties());

    mojo.useNativeGit = true;
    mojo.execute();
    Properties nativeProps = createCopy(targetProject.getProperties());

    assertGitPropertiesPresentInProject(jgitProps);
    assertGitPropertiesPresentInProject(nativeProps);

    for (String key : GIT_KEYS) {
      if (!key.equals("git.build.time")) {
        // git.build.time is excused because the two runs happened at different times.
        String jGitKey = jgitProps.getProperty(key);
        String nativeKey = nativeProps.getProperty(key);
        Assertions.assertEquals(
            jGitKey,
            nativeKey,
            "Key difference for key: '" + key + "'; jgit=" + jGitKey
                + "; nativeKey=" + nativeKey + "; for " + repo.getDir());
      } else {
        // Ensure that the date formats are parseable and within reason. If running all the git
        // commands on the
        // native provider takes more than 60 seconds, then something is seriously wrong.
        long jGitBuildTimeInMs = format.parse(jgitProps.getProperty(key)).getTime();
        long nativeBuildTimeInMs = format.parse(nativeProps.getProperty(key)).getTime();
        Assertions.assertTrue(
            jGitBuildTimeInMs <= nativeBuildTimeInMs,
            "Time ran backwards, jgitTime after nativeTime!");
        // If native takes more than 1 minute, something is wrong.
        Assertions.assertTrue(
            (nativeBuildTimeInMs - jGitBuildTimeInMs) < 60000L,
            "Build ran too slow.");
      }
    }

    // Check the commit time to be equal in ms, too.
    long jGitCommitTimeInMs = format.parse(jgitProps.getProperty("git.commit.time")).getTime();
    long nativeCommitTimeInMs = format.parse(nativeProps.getProperty("git.commit.time")).getTime();

    Assertions.assertEquals(
        jGitCommitTimeInMs,
        nativeCommitTimeInMs,
        "commit times parse to different time stamps");
  }

  private Properties createCopy(Properties orig) {
    Properties p = new Properties();
    for (String key : orig.stringPropertyNames()) {
      p.setProperty(key, orig.getProperty(key));
    }

    return p;
  }
}
